"use client";
import { CheckCircleIcon, XCircleIcon } from "lucide-react";
import type { ReactNode } from "react";
import { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { BsExclamationTriangleFill } from "react-icons/bs";
import { HiInformationCircle } from "react-icons/hi";
import { createContext, useContext } from "use-context-selector";

import classNames from "@/utils/classnames";

export type IToastProps = {
  type?: "success" | "error" | "warning" | "info";
  duration?: number;
  message: string;
  children?: ReactNode;
  onClose?: () => void;
  className?: string;
};
type IToastContext = {
  notify: (props: IToastProps) => void;
};

export const ToastContext = createContext<IToastContext>({} as IToastContext);
export const useToastContext = () => useContext(ToastContext);
const Toast = ({
  type = "info",
  message,
  children,
  className,
}: IToastProps) => {
  // sometimes message is react node array. Not handle it.
  if (typeof message !== "string") return null;

  return (
    <div
      className={classNames(
        className,
        "fixed rounded-md p-4 my-4 mx-8 z-[9999]",
        "top-0",
        "right-0",
        type === "success" ? "bg-green-50" : "",
        type === "error" ? "bg-red-50" : "",
        type === "warning" ? "bg-yellow-50" : "",
        type === "info" ? "bg-blue-50" : ""
      )}
    >
      <div className="flex">
        <div className="flex-shrink-0">
          {type === "success" && (
            <CheckCircleIcon
              className="w-5 h-5 text-green-400"
              aria-hidden="true"
            />
          )}
          {type === "error" && (
            <XCircleIcon className="w-5 h-5 text-red-400" aria-hidden="true" />
          )}
          {type === "warning" && (
            <BsExclamationTriangleFill
              className="w-5 h-5 text-yellow-400"
              aria-hidden="true"
            />
          )}
          {type === "info" && (
            <HiInformationCircle
              className="w-5 h-5 text-blue-400"
              aria-hidden="true"
            />
          )}
        </div>
        <div className="ml-3">
          <h3
            className={classNames(
              "text-sm font-medium",
              type === "success" ? "text-green-800" : "",
              type === "error" ? "text-red-800" : "",
              type === "warning" ? "text-yellow-800" : "",
              type === "info" ? "text-blue-800" : ""
            )}
          >
            {message}
          </h3>
          {children && (
            <div
              className={classNames(
                "mt-2 text-sm",
                type === "success" ? "text-green-700" : "",
                type === "error" ? "text-red-700" : "",
                type === "warning" ? "text-yellow-700" : "",
                type === "info" ? "text-blue-700" : ""
              )}
            >
              {children}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export const ToastProvider = ({ children }: { children: ReactNode }) => {
  const placeholder: IToastProps = {
    type: "info",
    message: "Toast message",
    duration: 6000,
  };
  const [params, setParams] = useState<IToastProps>(placeholder);
  const defaultDuring =
    params.type === "success" || params.type === "info" ? 3000 : 6000;
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    if (mounted) {
      setTimeout(() => {
        setMounted(false);
      }, params.duration || defaultDuring);
    }
  }, [defaultDuring, mounted, params.duration]);

  return (
    <ToastContext.Provider
      value={{
        notify: (props) => {
          setMounted(true);
          setParams(props);
        },
      }}
    >
      {mounted && <Toast {...params} />}
      {children}
    </ToastContext.Provider>
  );
};

Toast.notify = ({
  type,
  message,
  duration,
  className,
}: Pick<IToastProps, "type" | "message" | "duration" | "className">) => {
  const defaultDuring = type === "success" || type === "info" ? 3000 : 6000;

  if (typeof window === "object") {
    const holder = document.createElement("div");
    const root = createRoot(holder);

    root.render(
      <Toast
        type={type}
        message={message}
        duration={duration}
        className={className}
      />
    );
    document.body.appendChild(holder);
    setTimeout(() => {
      if (holder) holder.remove();
    }, duration || defaultDuring);
  }
};

export default Toast;
